package entity

import (
	"encoding/json"
	"errors"
	"fmt"
	"src/protoMsg"
	"src/zeus/bytestream"
	"src/zeus/common"
	"src/zeus/iserver"
	"src/zeus/logger"
	"src/zeus/msgdef"

	log "src/zeus/logger"
)

const (
	PackProp_Role uint8 = 1
	PackProp_AOI  uint8 = 2
)

// PropInfo 属性相关
type PropInfo struct {
	value interface{}

	syncFlag bool
	dbFlag   bool

	Def     *PropDef
	props   iserver.IEntityProps
	GenFunc func()
}

func NewPropInfo(def *PropDef, props iserver.IEntityProps) *PropInfo {
	prop := &PropInfo{
		value:    nil,
		syncFlag: false,
		dbFlag:   false,
		Def:      def,
		props:    props,
	}
	prop.init()
	return prop
}

func ClonePropInfo(src *PropInfo) *PropInfo {
	if src == nil {
		return nil
	}
	return &PropInfo{
		value:    src.value,
		syncFlag: false,
		dbFlag:   false,
		Def:      src.Def,
		props:    src.props,
	}
}

func (p *PropInfo) init() {
	if p.Def == nil {
		log.Error("属性初始化失败, Def为空")
		return
	}
	switch p.Def.TypeIndex {
	case common.Type_bool:
		p.value = false
	case common.Type_int8:
		p.value = int8(0)
	case common.Type_int16:
		p.value = int16(0)
	case common.Type_int32:
		p.value = int32(0)
	case common.Type_int64:
		p.value = int64(0)
	case common.Type_uint8:
		p.value = uint8(0)
	case common.Type_uint16:
		p.value = uint16(0)
	case common.Type_uint32:
		p.value = uint32(0)
	case common.Type_uint64:
		p.value = uint64(0)
	case common.Type_string:
		p.value = ""
	case common.Type_float32:
		p.value = float32(0)
	case common.Type_float64:
		p.value = float64(0)
	case common.Type_protoMsg:
		var err error
		p.value, err = p.Def.CreateInst()
		if err != nil {
			log.Error(err, p.Def)
			return
		}
	}
}

// 判断是不是初始数值
func (p *PropInfo) CheckValDef() (ok bool) {
	if p.Def == nil {
		log.Error("属性初始化失败, Def为空")
		return
	}
	switch v := p.value.(type) {
	case bool:
		return v == false
	case int8:
		return v == 0
	case int16:
		return v == 0
	case int32:
		return v == 0
	case int64:
		return v == 0
	case uint8:
		return v == 0
	case uint16:
		return v == 0
	case uint32:
		return v == 0
	case uint64:
		return v == 0
	case string:
		return v == ""
	case float32:
		return v == 0
	case float64:
		return v == 0
	}
	return
}

func (p *PropInfo) GetName() string {
	if p.Def == nil {
		return ""
	}

	return p.Def.Name
}

func (p *PropInfo) GetIndex() uint16 {
	if p.Def == nil {
		return 0
	}

	return p.Def.Index
}

func (p *PropInfo) GetValue() interface{} {
	return p.value
}

func (p *PropInfo) IsSyncDirty() bool {
	return p.syncFlag
}

func (p *PropInfo) IsSaveDirty() bool {
	return p.dbFlag
}

func (p *PropInfo) SetValue(value interface{}) bool {
	if p.Def == nil {
		panic(fmt.Errorf("no def file exist"))
	}

	if !p.Def.IsValidValue(value) {
		panic(fmt.Errorf("prop type error %s", p.GetName()))
	}

	// 这里不做检查，直接dirty，检查在逻辑层去做
	// if reflect.DeepEqual(p.value, value) {
	// 	return false
	// }

	p.value = value
	if p.props != nil {
		p.props.PropDirty(p.GetIndex())
	}

	return true
}

// 从数据原加载数据，所以没有标脏操作
func (p *PropInfo) LoadValue(value interface{}) {
	if p.Def == nil {
		panic(fmt.Errorf("no def file exist"))
	}

	if !p.Def.IsValidValue(value) {
		panic(fmt.Errorf("prop type error %s", p.GetName()))
	}

	p.value = value
}

// 设置标脏
func (p *PropInfo) SetDirty() {
	if p.props != nil {
		p.props.PropDirty(p.GetIndex())
	}
}

// GetValueStreamSize 获取某个属性需要的尺寸
func (p *PropInfo) GetValueStreamSize() int {
	s := 0
	st := p.Def.TypeIndex

	switch st {
	case common.Type_bool, common.Type_int8, common.Type_uint8:
		s = 1
	case common.Type_int16, common.Type_uint16:
		s = 2
	case common.Type_int32, common.Type_uint32, common.Type_float32:
		s = 4
	case common.Type_int64, common.Type_uint64, common.Type_float64:
		s = 8
	case common.Type_string:
		s = len(p.value.(string)) + 2
	case common.Type_protoMsg:
		// proto struct
		// 本身序列化的长度+写入字节流时需要额外2个字节标识自身的长度
		if iM, ok := p.value.(msgdef.IMsg); ok {
			s = iM.Size() + 2
		} else {
			log.Error("Convert proto struct failed", st)
		}
	default:
		log.Error("GetValueStreamSize failed index not exist ", st)
	}

	// 数据大小 + TypeIndex大小
	return s + 1
}

// WriteValueToStream 把属性值加入到ByteStream中
func (p *PropInfo) WriteValueToStream(bs *bytestream.ByteStream) error {
	err := bs.WriteInt8(int8(p.Def.TypeIndex))
	if err != nil {
		return err
	}
	switch p.Def.TypeIndex {
	case common.Type_bool:
		err = bs.WriteBool(bool(p.value.(bool)))
	case common.Type_int8:
		err = bs.WriteInt8(p.value.(int8))
	case common.Type_int16:
		err = bs.WriteInt16(p.value.(int16))
	case common.Type_int32:
		err = bs.WriteInt32(p.value.(int32))
	case common.Type_int64:
		err = bs.WriteInt64(p.value.(int64))
	case common.Type_uint8:
		err = bs.WriteByte(p.value.(byte))
	case common.Type_uint16:
		err = bs.WriteUInt16(p.value.(uint16))
	case common.Type_uint32:
		err = bs.WriteUInt32(p.value.(uint32))
	case common.Type_uint64:
		err = bs.WriteUInt64(p.value.(uint64))
	case common.Type_string:
		err = bs.WriteStr(p.value.(string))
	case common.Type_float32:
		err = bs.WriteFloat32(p.value.(float32))
	case common.Type_float64:
		err = bs.WriteFloat64(p.value.(float64))
	case common.Type_protoMsg:
		// proto struct
		if iM, ok := p.value.(msgdef.IMsg); ok {
			data := make([]byte, iM.Size())
			if _, err = iM.MarshalTo(data); err != nil {
				return err
			}
			err = bs.WriteBytes(data)
		} else {
			log.Error("entity Convert proto struct failed", p.Def.TypeIndex)
		}

	default:
		return errors.New(fmt.Sprintf("entity WriteValueToStream typeindex[%d] is not exist ", p.Def.TypeIndex))
	}

	return err
}

// ReadValueFromStream 从Stream中读取属性
func (p *PropInfo) ReadValueFromStream(bs *bytestream.ByteStream) (err error) {
	index, err := bs.ReadInt8()
	if err != nil {
		return err
	}
	if p.Def.TypeIndex != uint8(index) {
		logger.Fatal("同步过来的数据index不对。")
	}
	switch p.Def.TypeIndex {
	case common.Type_bool:
		p.value, err = bs.ReadBool()
		// err = bs.WriteBool(bool(p.value.(bool)))
	case common.Type_int8:
		p.value, err = bs.ReadInt8()
		// err = bs.WriteByte(byte(p.value.(int8)))
	case common.Type_int16:
		p.value, err = bs.ReadInt16()
		// err = bs.WriteUInt16(uint16(p.value.(int16)))
	case common.Type_int32:
		p.value, err = bs.ReadInt32()
		// err = bs.WriteUInt32(uint32(p.value.(int32)))
	case common.Type_int64:
		p.value, err = bs.ReadInt64()
		// err = bs.WriteUInt64(uint64(p.value.(int64)))
	case common.Type_uint8:
		p.value, err = bs.ReadByte()
		// err = bs.WriteByte(p.value.(byte))
	case common.Type_uint16:
		p.value, err = bs.ReadUInt16()
		// err = bs.WriteUInt16(p.value.(uint16))
	case common.Type_uint32:
		p.value, err = bs.ReadUInt32()
		// err = bs.WriteUInt32(p.value.(uint32))
	case common.Type_uint64:
		p.value, err = bs.ReadUInt64()
		// err = bs.WriteUInt64(p.value.(uint64))
	case common.Type_string:
		p.value, err = bs.ReadStr()
		// err = bs.WriteStr(p.value.(string))
	case common.Type_float32:
		p.value, err = bs.ReadFloat32()
		// err = bs.WriteFloat32(p.value.(float32))
	case common.Type_float64:
		p.value, err = bs.ReadFloat64()
		// err = bs.WriteFloat64(p.value.(float64))
	case common.Type_protoMsg:
		// proto struct
		var err error
		p.value, err = p.Def.CreateInst()
		if err != nil {
			return err
		}
		if iM, ok := p.value.(msgdef.IMsg); ok {
			data, _ := bs.ReadBytes()
			if err = iM.Unmarshal(data); err != nil {
				return err
			}
		} else {
			log.Error("entity Convert proto struct failed", p.Def.TypeIndex)
		}

	default:
		return errors.New(fmt.Sprintf("entity WriteValueToStream typeindex[%d] is not exist ", p.Def.TypeIndex))
	}
	return err
	// switch p.Def.TypeIndex {
	// case common.Type_bool:
	// 	err = bs.WriteBool(bool(p.value.(bool)))
	// case common.Type_int8:
	// 	err = bs.WriteByte(byte(p.value.(int8)))
	// case common.Type_int16:
	// 	err = bs.WriteUInt16(uint16(p.value.(int16)))
	// case common.Type_int32:
	// 	err = bs.WriteUInt32(uint32(p.value.(int32)))
	// case common.Type_int64:
	// 	err = bs.WriteUInt64(uint64(p.value.(int64)))
	// case common.Type_uint8:
	// 	err = bs.WriteByte(p.value.(byte))
	// case common.Type_uint16:
	// 	err = bs.WriteUInt16(p.value.(uint16))
	// case common.Type_uint32:
	// 	err = bs.WriteUInt32(p.value.(uint32))
	// case common.Type_uint64:
	// 	err = bs.WriteUInt64(p.value.(uint64))
	// case common.Type_string:
	// 	err = bs.WriteStr(p.value.(string))
	// case common.Type_float32:
	// 	err = bs.WriteFloat32(p.value.(float32))
	// case common.Type_float64:
	// 	err = bs.WriteFloat64(p.value.(float64))
	// case common.Type_protoMsg:
	// 	// proto struct
	// 	if iM, ok := p.value.(msgdef.IMsg); ok {
	// 		data := make([]byte, iM.Size())
	// 		if _, err = iM.MarshalTo(data); err != nil {
	// 			return err
	// 		}
	// 		err = bs.WriteBytes(data)
	// 	} else {
	// 		log.Error("entity Convert proto struct failed", p.Def.TypeIndex)
	// 	}

	// default:
	// 	return errors.New(fmt.Sprintf("entity WriteValueToStream typeindex[%d] is not exist ", p.Def.TypeIndex))
	// }

	// return nil
}

// PackValue 打包value 给Redis用
func (p *PropInfo) PackValue() []byte {
	switch p.Def.TypeIndex {
	case common.Type_int8,
		common.Type_int16,
		common.Type_int32,
		common.Type_int64,
		common.Type_uint8,
		common.Type_uint16,
		common.Type_uint32,
		common.Type_uint64,
		common.Type_float32,
		common.Type_float64,
		common.Type_bool,
		common.Type_string:
		return []byte(fmt.Sprintf("%v", p.value))
	case common.Type_protoMsg:
		iM, ok := p.value.(msgdef.IMsg)
		if ok {
			data := make([]byte, iM.Size())
			iM.MarshalTo(data)
			return data
		}

		return nil
	default:
		log.Error(fmt.Sprintf("entity PackValue typeindex[%d] is not exist ", p.Def.TypeIndex))
	}
	return nil
}

// UnPackValue 从Redis中恢复Value
func (p *PropInfo) UnPackValue(data interface{}) {

	switch p.Def.TypeIndex {
	case common.Type_bool:
		v, _ := common.NewStringAny(data).ToBool() // redis.Bool(data, nil)
		p.value = bool(v)
	case common.Type_int8:
		v, _ := common.NewStringAny(data).ToInt() // redis.Int(data, nil)
		p.value = int8(v)
	case common.Type_int16:
		v, _ := common.NewStringAny(data).ToInt() // redis.Int(data, nil)
		p.value = int16(v)
	case common.Type_int32:
		v, _ := common.NewStringAny(data).ToInt() // redis.Int(data, nil)
		p.value = int32(v)
	case common.Type_int64:
		p.value, _ = common.NewStringAny(data).ToInt64() //redis.Int64(data, nil)
	case common.Type_uint8:
		v, _ := common.NewStringAny(data).ToUint64() // redis.Int(data, nil)
		p.value = uint8(v)
	case common.Type_uint16:
		v, _ := common.NewStringAny(data).ToUint64() //redis.Int(data, nil)
		p.value = uint16(v)
	case common.Type_uint32:
		v, _ := common.NewStringAny(data).ToUint64() //redis.Uint64(data, nil)
		p.value = uint32(v)
	case common.Type_uint64:
		p.value, _ = common.NewStringAny(data).ToUint64() //redis.Uint64(data, nil)
	case common.Type_float32:
		v, _ := common.NewStringAny(data).ToFloat() // redis.Float64(data, nil)
		p.value = float32(v)
	case common.Type_float64:
		p.value, _ = common.NewStringAny(data).ToFloat() // redis.Float64(data, nil)
	case common.Type_string:
		p.value = common.NewStringAny(data).ToString() //redis.String(data, nil)
	case common.Type_protoMsg:
		if data != nil {
			iM, ok := p.value.(msgdef.IMsg)
			if ok {
				if err := iM.Unmarshal(data.([]byte)); err != nil {
					log.Error(err)
				}
			} else {
				log.Warn("entity Unsupport prop type", p.Def.TypeName)
			}
		}
	default:
		log.Error(errors.New(fmt.Sprintf("entity UnPackValue error TypeIndex[%d] is notExist .", p.Def.TypeIndex)))
	}
}

// PackPropsToProtobuf 把属列表打包成proto
func PackPropsToProtobuf(props []*PropInfo) *protoMsg.PropBaseSyncList {

	if len(props) == 0 {
		return nil
	}
	MsgList := &protoMsg.PropBaseSyncList{}
	for _, prop := range props {
		baseMsg := &protoMsg.SyncBaseInfo{
			PropValue: &protoMsg.SyncBaseInfo_BoolValue{},
		}
		baseMsg.Index = uint32(prop.Def.Index)
		switch prop.Def.TypeIndex {
		case common.Type_bool:
			baseMsg.PropValue = &protoMsg.SyncBaseInfo_BoolValue{
				BoolValue: (prop.value.(bool)),
			}
		case common.Type_int8:
			baseMsg.PropValue = &protoMsg.SyncBaseInfo_Int32Value{
				Int32Value: int32(prop.value.(int8)),
			}
		case common.Type_int16:
			baseMsg.PropValue = &protoMsg.SyncBaseInfo_Int32Value{
				Int32Value: int32(prop.value.(int16)),
			}
		case common.Type_int32:
			baseMsg.PropValue = &protoMsg.SyncBaseInfo_Int32Value{
				Int32Value: int32(prop.value.(int32)),
			}
		case common.Type_int64:
			baseMsg.PropValue = &protoMsg.SyncBaseInfo_Int64Value{
				Int64Value: int64(prop.value.(int64)),
			}
		case common.Type_uint8:
			baseMsg.PropValue = &protoMsg.SyncBaseInfo_Uint32Value{
				Uint32Value: uint32(prop.value.(uint8)),
			}
		case common.Type_uint16:
			baseMsg.PropValue = &protoMsg.SyncBaseInfo_Uint32Value{
				Uint32Value: uint32(prop.value.(uint16)),
			}
		case common.Type_uint32:
			baseMsg.PropValue = &protoMsg.SyncBaseInfo_Uint32Value{
				Uint32Value: uint32(prop.value.(uint32)),
			}
		case common.Type_uint64:
			baseMsg.PropValue = &protoMsg.SyncBaseInfo_Uint64Value{
				Uint64Value: uint64(prop.value.(uint64)),
			}
		case common.Type_string:
			baseMsg.PropValue = &protoMsg.SyncBaseInfo_StringValue{
				StringValue: string(prop.value.(string)),
			}
		case common.Type_float32:
			baseMsg.PropValue = &protoMsg.SyncBaseInfo_FloatValue{
				FloatValue: float32(prop.value.(float32)),
			}
			// baseMsg.PropValue.(*protoMsg.SyncBaseInfo_FloatValue).FloatValue = float32(prop.value.(float32))
		case common.Type_float64:
			baseMsg.PropValue = &protoMsg.SyncBaseInfo_DoubleValue{
				DoubleValue: float64(prop.value.(float64)),
			}
		case common.Type_protoMsg:
			baseMsg.PropValue = &protoMsg.SyncBaseInfo_Msg_Value{
				Msg_Value: prop.PackValue(),
			}
			// baseMsg.PropValue.(*protoMsg.SyncBaseInfo_Msg_Value).Msg_Value = prop.PackValue()
		}
		//props := MsgList.GetProp()
		//props = append(props, baseMsg)
		MsgList.Prop = append(MsgList.Prop, baseMsg)
	}
	return MsgList
}

// PackProps 打包属性
func PackProps(e IPropsContainer, packType uint8) *protoMsg.PropBaseSyncList {

	props := make([]*PropInfo, 0, 10)

	switch packType {
	case PackProp_Role:
		e.ForEachProp(func(prop *PropInfo) {
			if prop.Def.IsMRoleInterest && prop.Def.IsMain { //fixed
				//非自己管理的属性 是不需要发送给自己的客户端的.
				if !(prop.Def.SaveSrvType == iserver.GetSrvInst().GetSaveType() ||
					prop.Def.MainSrvType == iserver.GetSrvInst().GetSaveType()) {
					logger.Debug(logger.LogKey_Prop, prop.Def.Name, " 非本服务器 关心属性,不要给客户端下发属性.", prop)
					return
				}
				if prop.Def.Name == "PlayerGuildInfo" { //fixed log
					logger.Debug(logger.LogKey_Prop, "PlayerGuildInfo 本服务器 关心属性,给客户端下发属性.", prop)
				}
				if prop.GenFunc != nil {
					prop.GenFunc()
				}
				// //如果是初始值，就不同步了
				// if prop.CheckValDef() {
				// 	return
				// }
				props = append(props, prop)
			}
		})
	case PackProp_AOI:
		e.ForEachProp(func(prop *PropInfo) {
			if prop.Def.IsClientInterest && prop.Def.IsMain {
				if prop.GenFunc != nil {
					prop.GenFunc()
				}
				//如果是初始值，就不同步了
				if prop.CheckValDef() {
					return
				}
				props = append(props, prop)
			}
		})
	}

	return PackPropsToProtobuf(props)
}

func (p *PropInfo) String() string {
	bs, _ := json.Marshal(p.value)
	return fmt.Sprintf("PropInfo.Def :%s value:%s", p.Def, string(bs))
}
